import javax.swing.*;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Dimension;
import java.awt.Color;
import java.util.Vector;
import java.awt.event.*;
import java.lang.Math;

/**
 * Class for creating the Notebook object, which is the content component of the right side of the Courier system.
 * 
 * @author Chia-Chi (Teresa) Lee
 */
public class Notebook extends JPanel {
  
  //The panel's current height and width
  private int curr_height;
  private int curr_width;
  
  //The index of the current page
  private int curr_page;
  
  //Store a list of pages
  private Vector pages = new Vector();
  
  //Store any shape in progress
  private MyShape inProgressShape;
  
  //Drawing mode the program is in(oval, rectangle, or free-form)
  public String mode;
  
  //Store the current font family
  public String fontFamily;
  
  //Used for storing the text input cursor
  private int cursorX;
  private int cursorY;
  
  //Line height of the background
  final int LINE_HEIGHT = 30;
  
  /**
   * Initiate the Notebook object
   * 
   */
  public void init(){
    //Add mouse and key listeners
    MyMouseListener mouseListener = new MyMouseListener();
    MyKeyListener keyListener = new MyKeyListener();
    addMouseMotionListener(mouseListener);
    addMouseListener(mouseListener);
    addKeyListener(keyListener);
    
    setFocusable(true);
    
    //Create the first page
    Page p = new Page();
    pages.add(p);
    curr_page = 0;
  }
  
  /**
   * Custom key listener
   * 
   */
  private class MyKeyListener extends KeyAdapter {
    
    public void keyPressed(KeyEvent e) {
      
      //Create Text object if the text input cursor has been set
      if(inProgressShape == null && cursorX != -1){
       inProgressShape = new Text(cursorX,cursorY);
       ((Text)inProgressShape).setFont(fontFamily);
      }
      
      //Add character to the Text object
      if(cursorX != -1 && 32<=e.getKeyCode() && e.getKeyCode()<=126) {
       inProgressShape.add(e.getKeyChar());
       repaint();
      }
      
      //Remove character
      if(cursorX != -1 && ((Text)inProgressShape).getSize()>0 && e.getKeyCode()==8) {
       ((Text)inProgressShape).remove(((Text)inProgressShape).getSize()-1);
       repaint();
      }
    }
  }
  
  /**
   * Custom mouse listener
   * 
   */
  private class MyMouseListener extends MouseAdapter { 
    
     public void mousePressed(MouseEvent me) {
       
       //Move the in progress shape to the current page's display list
       if(inProgressShape != null)
       {
           ((Page)pages.get(curr_page)).addShapes(inProgressShape.clone());
           inProgressShape = null;
       }
       
       //Set text input cursor
       cursorX = me.getX();
       cursorY = nicePointerY(me.getY());
       
       requestFocus();
     }
     
     /**
      * Convert Y to a better location so the text is right above the line
      * 
      */ 
     public int nicePointerY(int meY){
       int Y = meY % LINE_HEIGHT;
       Y = (meY - Y) + LINE_HEIGHT - 5;
       return Y;
     }
          
     public void mouseDragged(MouseEvent me) {
       
       //If dragging occurs, do not accept text input
       cursorX = -1;
       
       //Create shapes according to the drawing mode if there is no in progress shape
       if(inProgressShape == null)
       {
          if(mode=="Rectangle"){
              inProgressShape = new Rectangle(me.getX(),me.getX(),me.getY(),me.getY());
          }
          else if(mode=="Oval"){
              inProgressShape = new Oval(me.getX(),me.getX(),me.getY(),me.getY());
          }
          else if(mode=="Free-Form Ink"){
              inProgressShape = new FreeForm(me.getX(),me.getY());
          }
       }
       
       //If there is in progress shape, simply update the end x and end y
       if(inProgressShape != null){
       inProgressShape.setX_e(me.getX());
       inProgressShape.setY_e(me.getY());
       }
       
       repaint();
    }
          
  }
  
  /**
   * Override paintComponent
   * 
   * @param g the Graphics2D object
   */
  @Override
  protected void paintComponent(Graphics g)
  {
    Graphics2D g2 = (Graphics2D) g;
    
    //Set anti-alising
    RenderingHints rh = g2.getRenderingHints ();
    rh.put (RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setRenderingHints (rh);
    
    //Set the current width and height of the window
    if(curr_height < getHeight()){
    curr_height = getHeight();
    }
    if(curr_width < getWidth()){
    curr_width = getWidth();
    }
    
    super.paintComponent(g); 
    
    //Paint the background
    paintBackground(g2);
    
    //Paint the shapes
    paintShapes(g2,curr_width,curr_height);
  }
  
  /**
   * Draw the background of the page
   * 
   * @param g the Graphics2D object
   */
  private void paintBackground(Graphics2D g){
    g.setColor(new Color(137,252,239));
    setBackground(new Color(253,253,158));
    for(int i=0; i<curr_height; i=i+LINE_HEIGHT){
        g.drawLine(0,i,curr_width,i);
    }
    g.setColor(Color.red);
    g.drawLine(50,0,50,curr_height);
  }
  
  /**
   * Draw the shapes on the page
   * 
   * @param g the Graphics2D object
   * @param screenWidth the width of the screen/window
   * @param screenHeight the height of the screen/window
   */
  public void paintShapes(Graphics2D g, int screenWidth, int screenHeight){
    g.setColor(Color.black);
    
    //Get the right-most and the bottom-most components. They are used to determine if scrollbars should appear
    int rightMost = 0;
    int bottomMost = 0;
    
    Vector shapes = new Vector();
    
    //Loop through the current page's display list
    if((shapes = ((Page)pages.get(curr_page)).getDisplayList()) != null)
    {
      MyShape current;
      for(int i=0; i<shapes.size();i++){
        current = (MyShape)shapes.get(i);
        current.drawMe(g, screenWidth, screenHeight);
        if(rightMost < current.getRightMost())
          rightMost = current.getRightMost();
        if(bottomMost < current.getBottomMost())
          bottomMost = current.getBottomMost();
      }
    }
    
    //Also draw the in progress shape
    if(inProgressShape != null)
    {
      inProgressShape.drawMe(g, screenWidth, screenHeight);
      if(rightMost < inProgressShape.getRightMost())
          rightMost = inProgressShape.getRightMost();
      if(bottomMost < inProgressShape.getBottomMost())
          bottomMost = inProgressShape.getBottomMost();
    }
    
    //Give 20px buffer
    if(rightMost+20 > curr_width)
      curr_width = rightMost+20;
    if(bottomMost+20 > curr_height)
      curr_height = bottomMost+20;
    setViewSize(curr_width,curr_height);
    revalidate();
  }
  
  /**
   * Set the preferred size of the drawing area
   * 
   * @param x width
   * @param y height
   */
  private void setViewSize(int x, int y){
    setPreferredSize(new Dimension(x,y));
  }
  
  /**
   * Create a new page
   * 
   */
  public void newPage(){
    
    //Add in progress shape to the current page
    if(inProgressShape != null)
    {
        ((Page)pages.get(curr_page)).addShapes(inProgressShape.clone());
        inProgressShape = null;
    }
    
    //Create new page
    Page p = new Page();
    pages.add(p);
    
    //Go to the new page
    curr_page = pages.size()-1;
    
    repaint();
  }
  
  /**
   * Delete a page
   * 
   */
  public void deletePage(){
    //Add in progress shape to the current page
    if(inProgressShape != null)
    {
        inProgressShape = null;
    }
    
    pages.remove(curr_page);
    
    //If there are no pages left, create a new one automatically so there will always be one page
    if(pages.size()==0){
      Page p = new Page();
      pages.add(p);
    }
    
    //Go to the previous page; do it only if the deleted page is not the first page
    if(pages.size()-1<curr_page){
     curr_page--; 
    }
    
    repaint();
  }
  
  /**
   * Go to the next page
   * 
   */
  public void nextPage(){
    //Add in progress shape to the current page
    if(inProgressShape != null)
    {
        ((Page)pages.get(curr_page)).addShapes(inProgressShape.clone());
        inProgressShape = null;
    }
    
   curr_page++;
   
   repaint();
  }
  
  /**
   * Go to the previous page
   * 
   */
  public void prevPage(){
    //Add in progress shape to the current page
    if(inProgressShape != null)
    {
        ((Page)pages.get(curr_page)).addShapes(inProgressShape.clone());
        inProgressShape = null;
    } 
    
    curr_page--;
    repaint();
  }
  
  /**
   * Check whether the back button should be disabled
   * 
   * @return true if back button should be enabled; false otherwise
   */
  public boolean isBackEnabled(){
    return curr_page > 0;
  }
  
  /**
   * Check whether the forward button should be disabled
   * 
   * @return true if forward button should be enabled; false otherwise
   */
  public boolean isForEnabled(){
    return curr_page < pages.size()-1;
  }
  
}